package baseFx.common.caching.redis;

import baseFx.common.Assert;
import baseFx.common.caching.Cache;
import baseFx.common.caching.CacheItem;
import baseFx.common.utils.ClassUtils;
import baseFx.common.utils.StringUtils;
import baseFx.serialization.json.JsonUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;

public class RedisCache implements Cache {
    public boolean enableSerializable = true;
    private static String hexPrefix = "$HEX-";
    private JedisPool pool;

    public RedisCache(JedisPool pool) {
        this.pool = pool;
    }

    protected String serialize(Object value) {
        return JsonUtils.toJson(value);
    }

    private String valueSerialize(Object value) {
        if (value instanceof String) {
            return (String) value;
        }
        if (enableSerializable && value instanceof Serializable) {
            String hex = ClassUtils.serialize(value);
            if (hex != null) {
                return hexPrefix + hex;
            }
        }
        return serialize(value);
    }

    private Object valueDeserialize(String value, Class<?> clazz) {
        if (StringUtils.isNullOrEmpty(value)) {
            return null;
        }
        if (clazz == String.class) {
            return value;
        }
        if (enableSerializable && value.startsWith(hexPrefix)) {
            value = value.substring(hexPrefix.length());
            return ClassUtils.deserialize(value);
        }
        return deserialize(value, clazz);
    }

    protected Object deserialize(String value, Class<?> clazz) {
        if (value.startsWith("[")) {
            return JsonUtils.toList(value, clazz);
        }
        return JsonUtils.toObject(value, clazz);
    }

    protected Jedis getClient() {
        return pool.getResource();
    }

    @Override
    public void save(CacheItem item) {
        Assert.isNotNull(item.value, "保存的值不能是空");
        Class<?> valueClass = item.value.getClass();
        boolean isArr = false;
        boolean isMap = false;
        if (valueClass.getTypeName().contains("$")) {
            Assert.isTrue(Modifier.isStatic(valueClass.getModifiers()), "内部类必须是静态的:" + valueClass.getTypeName());
        }
        if (valueClass.isArray()) {
            Assert.isNotEquals(0, ((Object[]) item.value).length, "不能保存元素为空的数组");
            valueClass = item.value.getClass().getComponentType();
            isArr = true;
        } else if (item.value instanceof List) {
            Assert.isFalse(((List<?>) item.value).isEmpty(), "不能保存元素为空的List");
            valueClass = ((List<?>) item.value).get(0).getClass();
        } else if (item.value instanceof Map) {
            valueClass = Map.class;
            isMap = true;
        }
        String value = valueSerialize(item.value);
        JedisCacheItem save = JedisCacheItem.convert(item, value, valueClass.getTypeName());
        save.isArray = isArr;
        save.isMap = isMap;
        String data = valueSerialize(save);
        Integer expire = item.absoluteExpiration != null ? item.absoluteExpiration : item.slidingExpiration;
        try (Jedis jedis = getClient()) {
            if (expire == null) {
                jedis.set(save.key, data);
            } else {
                jedis.setex(save.key, expire, data);
            }
        }
    }

    @Override
    public void remove(String key) {
        try (Jedis jedis = getClient()) {
            jedis.del(key);
        }
    }

    @Override
    public void update(String key, Object value) {
        try (Jedis jedis = getClient()) {
            String data = jedis.get(key);
            if (data == null) {
                return;
            }
            JedisCacheItem item = (JedisCacheItem) valueDeserialize(data, JedisCacheItem.class);
            CacheItem newItem = new CacheItem();
            newItem.key = key;
            newItem.value = value;
            newItem.slidingExpiration = item.slidingExpiration;
            newItem.absoluteExpiration = item.absoluteExpiration;
            save(newItem);
        }
    }

    @Override
    public boolean exists(String key) {
        try (Jedis jedis = getClient()) {
            return jedis.exists(key);
        }
    }

    @Override
    public Object get(String key) {
        try (Jedis jedis = getClient()) {
            String data = jedis.get(key);
            if (data == null) {
                return null;
            }
            JedisCacheItem item = (JedisCacheItem) valueDeserialize(data, JedisCacheItem.class);
            Class<?> clazz = ClassUtils.getClassForName(item.clazz);
            Object value = valueDeserialize(item.value, clazz);
            if (value instanceof List && item.isArray) {
                value = ((List<?>) value).toArray();
            }
            if (item.slidingExpiration != null) {
                jedis.expire(key, item.slidingExpiration);
            }
            return value;
        }
    }
}
